#include "Team.h"
#include "Ball.h"
#include "FieldPlayer.h"
#include "GoalKeeper.h"
#include "Messages.h"
#include "TeamStates.h"
#include "TelegramDispatcher.h"
#include "Utils.h"

using namespace std;

Team::Team(GameState* game, Params::TeamColor color) : m_game(game),
											m_color(color),
											m_distBallToHomeGoal(0.f),
											m_gkWithBall(false),
											m_userControlled(false),
											m_keyboardControlled(false),
											m_gamepadControlled(false),
											m_playerUserControlled(NULL)
{

	if (m_color==Params::blue)
	{
		m_homeGoal = game->BlueGoal();
		m_oppGoal = game->RedGoal();
	} 
	else
	{
		m_homeGoal = game->RedGoal();
		m_oppGoal = game->BlueGoal();
	}
	
	m_FSM = new StateMachine<Team>(this);
	m_FSM->SetGlobalState(NULL);
	m_FSM->SetCurrentState(NULL);
	m_FSM->SetPreviousState(NULL);

	CreatePlayers();

}

Team::~Team(void)
{
	DeleteSTLContainer(m_players);
	delete m_FSM;
}

/* Method to create team's players */
void Team::CreatePlayers()
{
	if (m_color==Params::blue)
	{
		sf::Color playerColor = sf::Color(0,0,255,255);
		m_players.push_back( new GoalKeeper(m_game, this, 16, sf::Color(0,150,100)));
		m_players.push_back( new FieldPlayer(m_game, this, Params::BlueDefRegion[0], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::BlueDefRegion[1], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::BlueDefRegion[2], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::BlueDefRegion[3], playerColor) );
	} 
	else
	{
		sf::Color playerColor = sf::Color(255,0,0,255);
		m_players.push_back( new GoalKeeper(m_game, this, 1, sf::Color(120,70,80)));
		m_players.push_back( new FieldPlayer(m_game, this, Params::RedDefRegion[0], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::RedDefRegion[1], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::RedDefRegion[2], playerColor) );
		m_players.push_back( new FieldPlayer(m_game, this, Params::RedDefRegion[3], playerColor) );
	}
}

/* Change players home regions with the given regions*/
void Team::ChangeRegions(const int* regions)
{
	//Starting value of i is 1 because the first player in list is the gk.
	for (unsigned int i = 1; i < m_players.size(); i++)
	{
		m_players[i]->SetHomeRegion(regions[i-1]);
	}

}

/* Well, like it says */
void Team::SendPlayersToHome()
{
	for (vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it)
	{
		(*it)->ToHome();
	}
}

/*Prepare team's players for KickOff*/
void Team::PrepareForKickOff(bool myTurn)
{
	m_FSM->ChangeState(KickOff::Istance());

	if(myTurn)	
	{
		m_TPlayerController = m_players[4];
		ChangeRegions( (m_color==Params::blue) ? Params::BlueKickOffRegion : Params::RedKickOffRegion);
	}
	else
	{
		m_TPlayerController = NULL;
		ChangeRegions( (m_color==Params::blue) ? Params::BlueDefRegion : Params::RedDefRegion);
	}

	for (vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it)
	{
		TelegramDispatcher::Istance()->DispatchTelegram( NULL, (*it)->ID(), PrepareKickOff, 0.f );
	}
	
}

/*  Check if the team is ready to KickOff (start the game) */
bool Team::ReadyForKickOff()
{
	for (vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it )
	{
		if (!(*it)->AtTarget())
			return false;
	}
	return true;
}

/* If team is in charge for KickOff, start the game. */
void Team::KickOff()
{
	if (InControl())
	{
		Ball::Istance()->CapturedBy(m_TPlayerController);
		int passTo = RandBetween<int>( 1, 3); 
		ControllingPlayer()->Pass(  m_players[passTo] );

		if(m_userControlled) SetPlayerUserControlled( m_players[passTo]);
	}
	else
	{
		if(m_userControlled) SetPlayerUserControlled(m_TPlayerClosestBall);
	}
}

void Team::SetPlayerUserControlled(BasePlayer* bp)
{
	if(bp) bp->SetUserControl(true);
	m_playerUserControlled = bp;
}

/* Team Update.*/
void Team::Update(const sf::Input& input)
{	
	m_distBallToHomeGoal = (Ball::Istance()->GetPosition() - m_homeGoal->Center()).Length();

	//Calculate some usefull infos. TeamPlayerClosestToBall, TPlayerClosestToHomeGoal and TPlayerClosestToOppGoal
	float dToBall = 0.f;
	float dToBall2 = 0.f;
	float dToHome = 0.f;
	float dToOpp = 0.f;
	for(vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it )
	{
		if( (*it)->IsWaiting() ) continue;

		if (dToBall == 0.f || dToBall > (*it)->DistToBall())
		{
			if( m_TPlayerController!=(*it) || 
				( m_TPlayerController && m_TPlayerController==(*it) && !Ball::Istance()->IsControlled())){

				if (dToBall2 == 0.f)
				{
					dToBall2 = (*it)->DistToBall();
					m_TSecondPlayerClosestBall = *it;
				}
				else
				{
					dToBall2 = dToBall;
					m_TSecondPlayerClosestBall = m_TPlayerClosestBall;
				}
				dToBall = (*it)->DistToBall();
				m_TPlayerClosestBall = *it;
			}
		}
		else
		{
			if( (
				m_TPlayerController!=(*it) 
				|| ( m_TPlayerController && m_TPlayerController==(*it) && !Ball::Istance()->IsControlled())
				)
				&& (m_TSecondPlayerClosestBall == m_TPlayerClosestBall || dToBall2 > (*it)->DistToBall()))

					m_TSecondPlayerClosestBall = *it;
		}

		if (dToHome == 0.f || dToHome > (*it)->DistToHomeGoal())
		{
			dToHome = (*it)->DistToHomeGoal();
			m_TPlayerClosestToHomeGoal = *it;
		}

		if (dToOpp == 0.f || dToOpp > (*it)->DistToOppGoal())
		{
			dToOpp = (*it)->DistToOppGoal();
			m_TPlayerClosestToOppGoal = *it;
		}
	}

	//Update each player's finite state machine.
	for(vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it )
	{
		(*it)->Update(input);
	}
	
	//Update Team's finite state machine.
	m_FSM->Update();

	
}

void Team::OnDraw(sf::RenderWindow &window)
{
	for(vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it )
	{
		(*it)->OnDraw(window);
	}
}

void Team::DrawOnRadar(sf::RenderWindow &window, const sf::Vector2f &offset)
{
	for(vector<BasePlayer*>::iterator it = m_players.begin(); it!=m_players.end(); ++it )
	{
		(*it)->DrawOnRadar(window, offset);
	}
}